/*!
 * HTML5 export buttons for Buttons and DataTables.
 * 2016 SpryMedia Ltd - datatables.net/license
 *
 * FileSaver.js (1.3.3) - MIT license
 * Copyright © 2016 Eli Grey - http://eligrey.com
 */

(function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD
    define(['jquery', 'datatables.net', 'datatables.net-buttons'], function ($) {
      return factory($, window, document);
    });
  } else if (typeof exports === 'object') {
    // CommonJS
    module.exports = function (root, $, jszip, pdfmake) {
      if (!root) {
        root = window;
      }

      if (!$ || !$.fn.dataTable) {
        $ = require('datatables.net')(root, $).$;
      }

      if (!$.fn.dataTable.Buttons) {
        require('datatables.net-buttons')(root, $);
      }

      return factory($, root, root.document, jszip, pdfmake);
    };
  } else {
    // Browser
    factory(jQuery, window, document);
  }
}(function ($, window, document, jszip, pdfmake, undefined) {
  'use strict';
  var DataTable = $.fn.dataTable;

// Allow the constructor to pass in JSZip and PDFMake from external requires.
// Otherwise, use globally defined variables, if they are available.
  function _jsZip() {
    return jszip || window.JSZip;
  }

  function _pdfMake() {
    return pdfmake || window.pdfMake;
  }

  DataTable.Buttons.pdfMake = function (_) {
    if (!_) {
      return _pdfMake();
    }
    pdfmake = _;
  }

  DataTable.Buttons.jszip = function (_) {
    if (!_) {
      return _jsZip();
    }
    jszip = _;
  }


  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   * FileSaver.js dependency
   */

  /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */

  var _saveAs = (function (view) {
    "use strict";
    // IE <10 is explicitly unsupported
    if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
      return;
    }
    var
      doc = view.document
      // only get URL when necessary in case Blob.js hasn't overridden it yet
      , get_URL = function () {
        return view.URL || view.webkitURL || view;
      }
      , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
      , can_use_save_link = "download" in save_link
      , click = function (node) {
        var event = new MouseEvent("click");
        node.dispatchEvent(event);
      }
      , is_safari = /constructor/i.test(view.HTMLElement) || view.safari
      , is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent)
      , throw_outside = function (ex) {
        (view.setImmediate || view.setTimeout)(function () {
          throw ex;
        }, 0);
      }
      , force_saveable_type = "application/octet-stream"
      // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
      , arbitrary_revoke_timeout = 1000 * 40 // in ms
      , revoke = function (file) {
        var revoker = function () {
          if (typeof file === "string") { // file is an object URL
            get_URL().revokeObjectURL(file);
          } else { // file is a File
            file.remove();
          }
        };
        setTimeout(revoker, arbitrary_revoke_timeout);
      }
      , dispatch = function (filesaver, event_types, event) {
        event_types = [].concat(event_types);
        var i = event_types.length;
        while (i--) {
          var listener = filesaver["on" + event_types[i]];
          if (typeof listener === "function") {
            try {
              listener.call(filesaver, event || filesaver);
            } catch (ex) {
              throw_outside(ex);
            }
          }
        }
      }
      , auto_bom = function (blob) {
        // prepend BOM for UTF-8 XML and text/* types (including HTML)
        // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
        if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
          return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
        }
        return blob;
      }
      , FileSaver = function (blob, name, no_auto_bom) {
        if (!no_auto_bom) {
          blob = auto_bom(blob);
        }
        // First try a.download, then web filesystem, then object URLs
        var
          filesaver = this
          , type = blob.type
          , force = type === force_saveable_type
          , object_url
          , dispatch_all = function () {
            dispatch(filesaver, "writestart progress write writeend".split(" "));
          }
          // on any filesys errors revert to saving with object URLs
          , fs_error = function () {
            if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
              // Safari doesn't allow downloading of blob urls
              var reader = new FileReader();
              reader.onloadend = function () {
                var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
                var popup = view.open(url, '_blank');
                if (!popup) view.location.href = url;
                url = undefined; // release reference before dispatching
                filesaver.readyState = filesaver.DONE;
                dispatch_all();
              };
              reader.readAsDataURL(blob);
              filesaver.readyState = filesaver.INIT;
              return;
            }
            // don't create more object URLs than needed
            if (!object_url) {
              object_url = get_URL().createObjectURL(blob);
            }
            if (force) {
              view.location.href = object_url;
            } else {
              var opened = view.open(object_url, "_blank");
              if (!opened) {
                // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
                view.location.href = object_url;
              }
            }
            filesaver.readyState = filesaver.DONE;
            dispatch_all();
            revoke(object_url);
          }
        ;
        filesaver.readyState = filesaver.INIT;

        if (can_use_save_link) {
          object_url = get_URL().createObjectURL(blob);
          setTimeout(function () {
            save_link.href = object_url;
            save_link.download = name;
            click(save_link);
            dispatch_all();
            revoke(object_url);
            filesaver.readyState = filesaver.DONE;
          });
          return;
        }

        fs_error();
      }
      , FS_proto = FileSaver.prototype
      , saveAs = function (blob, name, no_auto_bom) {
        return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
      }
    ;
    // IE 10+ (native saveAs)
    if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
      return function (blob, name, no_auto_bom) {
        name = name || blob.name || "download";

        if (!no_auto_bom) {
          blob = auto_bom(blob);
        }
        return navigator.msSaveOrOpenBlob(blob, name);
      };
    }

    FS_proto.abort = function () {
    };
    FS_proto.readyState = FS_proto.INIT = 0;
    FS_proto.WRITING = 1;
    FS_proto.DONE = 2;

    FS_proto.error =
      FS_proto.onwritestart =
        FS_proto.onprogress =
          FS_proto.onwrite =
            FS_proto.onabort =
              FS_proto.onerror =
                FS_proto.onwriteend =
                  null;

    return saveAs;
  }(
    typeof self !== "undefined" && self
    || typeof window !== "undefined" && window
    || this.content
  ));


// Expose file saver on the DataTables API. Can't attach to `DataTables.Buttons`
// since this file can be loaded before Button's core!
  DataTable.fileSave = _saveAs;


  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   * Local (private) functions
   */

  /**
   * Get the sheet name for Excel exports.
   *
   * @param {object}  config Button configuration
   */
  var _sheetname = function (config) {
    var sheetName = 'Sheet1';

    if (config.sheetName) {
      sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, '');
    }

    return sheetName;
  };

  /**
   * Get the newline character(s)
   *
   * @param {object}  config Button configuration
   * @return {string}        Newline character
   */
  var _newLine = function (config) {
    return config.newline ?
      config.newline :
      navigator.userAgent.match(/Windows/) ?
        '\r\n' :
        '\n';
  };

  /**
   * Combine the data from the `buttons.exportData` method into a string that
   * will be used in the export file.
   *
   * @param  {DataTable.Api} dt     DataTables API instance
   * @param  {object}        config Button configuration
   * @return {object}               The data to export
   */
  var _exportData = function (dt, config) {
    var newLine = _newLine(config);
    var data = dt.buttons.exportData(config.exportOptions);
    var boundary = config.fieldBoundary;
    var separator = config.fieldSeparator;
    var reBoundary = new RegExp(boundary, 'g');
    var escapeChar = config.escapeChar !== undefined ?
      config.escapeChar :
      '\\';
    var join = function (a) {
      var s = '';

      // If there is a field boundary, then we might need to escape it in
      // the source data
      for (var i = 0, ien = a.length; i < ien; i++) {
        if (i > 0) {
          s += separator;
        }

        s += boundary ?
          boundary + ('' + a[i]).replace(reBoundary, escapeChar + boundary) + boundary :
          a[i];
      }

      return s;
    };

    var header = config.header ? join(data.header) + newLine : '';
    var footer = config.footer && data.footer ? newLine + join(data.footer) : '';
    var body = [];

    for (var i = 0, ien = data.body.length; i < ien; i++) {
      body.push(join(data.body[i]));
    }

    return {
      str: header + body.join(newLine) + footer,
      rows: body.length
    };
  };

  /**
   * Older versions of Safari (prior to tech preview 18) don't support the
   * download option required.
   *
   * @return {Boolean} `true` if old Safari
   */
  var _isDuffSafari = function () {
    var safari = navigator.userAgent.indexOf('Safari') !== -1 &&
      navigator.userAgent.indexOf('Chrome') === -1 &&
      navigator.userAgent.indexOf('Opera') === -1;

    if (!safari) {
      return false;
    }

    var version = navigator.userAgent.match(/AppleWebKit\/(\d+\.\d+)/);
    if (version && version.length > 1 && version[1] * 1 < 603.1) {
      return true;
    }

    return false;
  };

  /**
   * Convert from numeric position to letter for column names in Excel
   * @param  {int} n Column number
   * @return {string} Column letter(s) name
   */
  function createCellPos(n) {
    var ordA = 'A'.charCodeAt(0);
    var ordZ = 'Z'.charCodeAt(0);
    var len = ordZ - ordA + 1;
    var s = "";

    while (n >= 0) {
      s = String.fromCharCode(n % len + ordA) + s;
      n = Math.floor(n / len) - 1;
    }

    return s;
  }

  try {
    var _serialiser = new XMLSerializer();
    var _ieExcel;
  } catch (t) {
  }

  /**
   * Recursively add XML files from an object's structure to a ZIP file. This
   * allows the XSLX file to be easily defined with an object's structure matching
   * the files structure.
   *
   * @param {JSZip} zip ZIP package
   * @param {object} obj Object to add (recursive)
   */
  function _addToZip(zip, obj) {
    if (_ieExcel === undefined) {
      // Detect if we are dealing with IE's _awful_ serialiser by seeing if it
      // drop attributes
      _ieExcel = _serialiser
        .serializeToString(
          (new window.DOMParser()).parseFromString(excelStrings['xl/worksheets/sheet1.xml'], 'text/xml')
        )
        .indexOf('xmlns:r') === -1;
    }

    $.each(obj, function (name, val) {
      if ($.isPlainObject(val)) {
        var newDir = zip.folder(name);
        _addToZip(newDir, val);
      } else {
        if (_ieExcel) {
          // IE's XML serialiser will drop some name space attributes from
          // from the root node, so we need to save them. Do this by
          // replacing the namespace nodes with a regular attribute that
          // we convert back when serialised. Edge does not have this
          // issue
          var worksheet = val.childNodes[0];
          var i, ien;
          var attrs = [];

          for (i = worksheet.attributes.length - 1; i >= 0; i--) {
            var attrName = worksheet.attributes[i].nodeName;
            var attrValue = worksheet.attributes[i].nodeValue;

            if (attrName.indexOf(':') !== -1) {
              attrs.push({name: attrName, value: attrValue});

              worksheet.removeAttribute(attrName);
            }
          }

          for (i = 0, ien = attrs.length; i < ien; i++) {
            var attr = val.createAttribute(attrs[i].name.replace(':', '_dt_b_namespace_token_'));
            attr.value = attrs[i].value;
            worksheet.setAttributeNode(attr);
          }
        }

        var str = _serialiser.serializeToString(val);

        // Fix IE's XML
        if (_ieExcel) {
          // IE doesn't include the XML declaration
          if (str.indexOf('<?xml') === -1) {
            str = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + str;
          }

          // Return namespace attributes to being as such
          str = str.replace(/_dt_b_namespace_token_/g, ':');

          // Remove testing name space that IE puts into the space preserve attr
          str = str.replace(/xmlns:NS[\d]+="" NS[\d]+:/g, '');
        }

        // Safari, IE and Edge will put empty name space attributes onto
        // various elements making them useless. This strips them out
        str = str.replace(/<([^<>]*?) xmlns=""([^<>]*?)>/g, '<$1 $2>');

        zip.file(name, str);
      }
    });
  }

  /**
   * Create an XML node and add any children, attributes, etc without needing to
   * be verbose in the DOM.
   *
   * @param  {object} doc      XML document
   * @param  {string} nodeName Node name
   * @param  {object} opts     Options - can be `attr` (attributes), `children`
   *   (child nodes) and `text` (text content)
   * @return {node}            Created node
   */
  function _createNode(doc, nodeName, opts) {
    var tempNode = doc.createElement(nodeName);

    if (opts) {
      if (opts.attr) {
        $(tempNode).attr(opts.attr);
      }

      if (opts.children) {
        $.each(opts.children, function (key, value) {
          tempNode.appendChild(value);
        });
      }

      if (opts.text !== null && opts.text !== undefined) {
        tempNode.appendChild(doc.createTextNode(opts.text));
      }
    }

    return tempNode;
  }

  /**
   * Get the width for an Excel column based on the contents of that column
   * @param  {object} data Data for export
   * @param  {int}    col  Column index
   * @return {int}         Column width
   */
  function _excelColWidth(data, col) {
    var max = data.header[col].length;
    var len, lineSplit, str;

    if (data.footer && data.footer[col].length > max) {
      max = data.footer[col].length;
    }

    for (var i = 0, ien = data.body.length; i < ien; i++) {
      var point = data.body[i][col];
      str = point !== null && point !== undefined ?
        point.toString() :
        '';

      // If there is a newline character, workout the width of the column
      // based on the longest line in the string
      if (str.indexOf('\n') !== -1) {
        lineSplit = str.split('\n');
        lineSplit.sort(function (a, b) {
          return b.length - a.length;
        });

        len = lineSplit[0].length;
      } else {
        len = str.length;
      }

      if (len > max) {
        max = len;
      }

      // Max width rather than having potentially massive column widths
      if (max > 40) {
        return 54; // 40 * 1.35
      }
    }

    max *= 1.35;

    // And a min width
    return max > 6 ? max : 6;
  }

// Excel - Pre-defined strings to build a basic XLSX file
  var excelStrings = {
    "_rels/.rels":
      '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
      '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
      '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
      '</Relationships>',

    "xl/_rels/workbook.xml.rels":
      '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
      '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
      '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>' +
      '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' +
      '</Relationships>',

    "[Content_Types].xml":
      '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
      '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
      '<Default Extension="xml" ContentType="application/xml" />' +
      '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />' +
      '<Default Extension="jpeg" ContentType="image/jpeg" />' +
      '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />' +
      '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />' +
      '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />' +
      '</Types>',

    "xl/workbook.xml":
      '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
      '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' +
      '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>' +
      '<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>' +
      '<bookViews>' +
      '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>' +
      '</bookViews>' +
      '<sheets>' +
      '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>' +
      '</sheets>' +
      '<definedNames/>' +
      '</workbook>',

    "xl/worksheets/sheet1.xml":
      '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
      '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">' +
      '<sheetData/>' +
      '<mergeCells count="0"/>' +
      '</worksheet>',

    "xl/styles.xml":
      '<?xml version="1.0" encoding="UTF-8"?>' +
      '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">' +
      '<numFmts count="6">' +
      '<numFmt numFmtId="164" formatCode="#,##0.00_-\ [$$-45C]"/>' +
      '<numFmt numFmtId="165" formatCode="&quot;£&quot;#,##0.00"/>' +
      '<numFmt numFmtId="166" formatCode="[$€-2]\ #,##0.00"/>' +
      '<numFmt numFmtId="167" formatCode="0.0%"/>' +
      '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>' +
      '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>' +
      '</numFmts>' +
      '<fonts count="5" x14ac:knownFonts="1">' +
      '<font>' +
      '<sz val="11" />' +
      '<name val="Calibri" />' +
      '</font>' +
      '<font>' +
      '<sz val="11" />' +
      '<name val="Calibri" />' +
      '<color rgb="FFFFFFFF" />' +
      '</font>' +
      '<font>' +
      '<sz val="11" />' +
      '<name val="Calibri" />' +
      '<b />' +
      '</font>' +
      '<font>' +
      '<sz val="11" />' +
      '<name val="Calibri" />' +
      '<i />' +
      '</font>' +
      '<font>' +
      '<sz val="11" />' +
      '<name val="Calibri" />' +
      '<u />' +
      '</font>' +
      '</fonts>' +
      '<fills count="6">' +
      '<fill>' +
      '<patternFill patternType="none" />' +
      '</fill>' +
      '<fill>' + // Excel appears to use this as a dotted background regardless of values but
      '<patternFill patternType="none" />' + // to be valid to the schema, use a patternFill
      '</fill>' +
      '<fill>' +
      '<patternFill patternType="solid">' +
      '<fgColor rgb="FFD9D9D9" />' +
      '<bgColor indexed="64" />' +
      '</patternFill>' +
      '</fill>' +
      '<fill>' +
      '<patternFill patternType="solid">' +
      '<fgColor rgb="FFD99795" />' +
      '<bgColor indexed="64" />' +
      '</patternFill>' +
      '</fill>' +
      '<fill>' +
      '<patternFill patternType="solid">' +
      '<fgColor rgb="ffc6efce" />' +
      '<bgColor indexed="64" />' +
      '</patternFill>' +
      '</fill>' +
      '<fill>' +
      '<patternFill patternType="solid">' +
      '<fgColor rgb="ffc6cfef" />' +
      '<bgColor indexed="64" />' +
      '</patternFill>' +
      '</fill>' +
      '</fills>' +
      '<borders count="2">' +
      '<border>' +
      '<left />' +
      '<right />' +
      '<top />' +
      '<bottom />' +
      '<diagonal />' +
      '</border>' +
      '<border diagonalUp="false" diagonalDown="false">' +
      '<left style="thin">' +
      '<color auto="1" />' +
      '</left>' +
      '<right style="thin">' +
      '<color auto="1" />' +
      '</right>' +
      '<top style="thin">' +
      '<color auto="1" />' +
      '</top>' +
      '<bottom style="thin">' +
      '<color auto="1" />' +
      '</bottom>' +
      '<diagonal />' +
      '</border>' +
      '</borders>' +
      '<cellStyleXfs count="1">' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />' +
      '</cellStyleXfs>' +
      '<cellXfs count="68">' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
      '<alignment horizontal="left"/>' +
      '</xf>' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
      '<alignment horizontal="center"/>' +
      '</xf>' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
      '<alignment horizontal="right"/>' +
      '</xf>' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
      '<alignment horizontal="fill"/>' +
      '</xf>' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
      '<alignment textRotation="90"/>' +
      '</xf>' +
      '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
      '<alignment wrapText="1"/>' +
      '</xf>' +
      '<xf numFmtId="9"   fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="1" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="2" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '<xf numFmtId="14" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
      '</cellXfs>' +
      '<cellStyles count="1">' +
      '<cellStyle name="Normal" xfId="0" builtinId="0" />' +
      '</cellStyles>' +
      '<dxfs count="0" />' +
      '<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />' +
      '</styleSheet>'
  };
// Note we could use 3 `for` loops for the styles, but when gzipped there is
// virtually no difference in size, since the above can be easily compressed

// Pattern matching for special number formats. Perhaps this should be exposed
// via an API in future?
// Ref: section 3.8.30 - built in formatters in open spreadsheet
//   https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
  var _excelSpecials = [
    {
      match: /^\-?\d+\.\d%$/, style: 60, fmt: function (d) {
        return d / 100;
      }
    }, // Precent with d.p.
    {
      match: /^\-?\d+\.?\d*%$/, style: 56, fmt: function (d) {
        return d / 100;
      }
    }, // Percent
    {match: /^\-?\$[\d,]+.?\d*$/, style: 57}, // Dollars
    {match: /^\-?£[\d,]+.?\d*$/, style: 58}, // Pounds
    {match: /^\-?€[\d,]+.?\d*$/, style: 59}, // Euros
    {match: /^\-?\d+$/, style: 65}, // Numbers without thousand separators
    {match: /^\-?\d+\.\d{2}$/, style: 66}, // Numbers 2 d.p. without thousands separators
    {
      match: /^\([\d,]+\)$/, style: 61, fmt: function (d) {
        return -1 * d.replace(/[\(\)]/g, '');
      }
    },  // Negative numbers indicated by brackets
    {
      match: /^\([\d,]+\.\d{2}\)$/, style: 62, fmt: function (d) {
        return -1 * d.replace(/[\(\)]/g, '');
      }
    },  // Negative numbers indicated by brackets - 2d.p.
    {match: /^\-?[\d,]+$/, style: 63}, // Numbers with thousand separators
    {match: /^\-?[\d,]+\.\d{2}$/, style: 64},
    {
      match: /^[\d]{4}\-[\d]{2}\-[\d]{2}$/, style: 67, fmt: function (d) {
        return Math.round(25569 + (Date.parse(d) / (86400 * 1000)));
      }
    } //Date yyyy-mm-dd
  ];


  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   * Buttons
   */

//
// Copy to clipboard
//
  DataTable.ext.buttons.copyHtml5 = {
    className: 'buttons-copy buttons-html5',

    text: function (dt) {
      return dt.i18n('buttons.copy', 'Copy');
    },

    action: function (e, dt, button, config) {
      this.processing(true);

      var that = this;
      var exportData = _exportData(dt, config);
      var info = dt.buttons.exportInfo(config);
      var newline = _newLine(config);
      var output = exportData.str;
      var hiddenDiv = $('<div/>')
        .css({
          height: 1,
          width: 1,
          overflow: 'hidden',
          position: 'fixed',
          top: 0,
          left: 0
        });

      if (info.title) {
        output = info.title + newline + newline + output;
      }

      if (info.messageTop) {
        output = info.messageTop + newline + newline + output;
      }

      if (info.messageBottom) {
        output = output + newline + newline + info.messageBottom;
      }

      if (config.customize) {
        output = config.customize(output, config, dt);
      }

      var textarea = $('<textarea readonly/>')
        .val(output)
        .appendTo(hiddenDiv);

      // For browsers that support the copy execCommand, try to use it
      if (document.queryCommandSupported('copy')) {
        hiddenDiv.appendTo(dt.table().container());
        textarea[0].focus();
        textarea[0].select();

        try {
          var successful = document.execCommand('copy');
          hiddenDiv.remove();

          if (successful) {
            dt.buttons.info(
              dt.i18n('buttons.copyTitle', 'Copy to clipboard'),
              dt.i18n('buttons.copySuccess', {
                1: 'Copied one row to clipboard',
                _: 'Copied %d rows to clipboard'
              }, exportData.rows),
              2000
            );

            this.processing(false);
            return;
          }
        } catch (t) {
        }
      }

      // Otherwise we show the text box and instruct the user to use it
      var message = $('<span>' + dt.i18n('buttons.copyKeys',
        'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>' +
        'To cancel, click this message or press escape.') + '</span>'
      )
        .append(hiddenDiv);

      dt.buttons.info(dt.i18n('buttons.copyTitle', 'Copy to clipboard'), message, 0);

      // Select the text so when the user activates their system clipboard
      // it will copy that text
      textarea[0].focus();
      textarea[0].select();

      // Event to hide the message when the user is done
      var container = $(message).closest('.dt-button-info');
      var close = function () {
        container.off('click.buttons-copy');
        $(document).off('.buttons-copy');
        dt.buttons.info(false);
      };

      container.on('click.buttons-copy', close);
      $(document)
        .on('keydown.buttons-copy', function (e) {
          if (e.keyCode === 27) { // esc
            close();
            that.processing(false);
          }
        })
        .on('copy.buttons-copy cut.buttons-copy', function () {
          close();
          that.processing(false);
        });
    },

    exportOptions: {},

    fieldSeparator: '\t',

    fieldBoundary: '',

    header: true,

    footer: false,

    title: '*',

    messageTop: '*',

    messageBottom: '*'
  };

//
// CSV export
//
  DataTable.ext.buttons.csvHtml5 = {
    bom: false,

    className: 'buttons-csv buttons-html5',

    available: function () {
      return window.FileReader !== undefined && window.Blob;
    },

    text: function (dt) {
      return dt.i18n('buttons.csv', 'CSV');
    },

    action: function (e, dt, button, config) {
      this.processing(true);

      // Set the text
      var output = _exportData(dt, config).str;
      var info = dt.buttons.exportInfo(config);
      var charset = config.charset;

      if (config.customize) {
        output = config.customize(output, config, dt);
      }

      if (charset !== false) {
        if (!charset) {
          charset = document.characterSet || document.charset;
        }

        if (charset) {
          charset = ';charset=' + charset;
        }
      } else {
        charset = '';
      }

      if (config.bom) {
        output = '\ufeff' + output;
      }

      _saveAs(
        new Blob([output], {type: 'text/csv' + charset}),
        info.filename,
        true
      );

      this.processing(false);
    },

    filename: '*',

    extension: '.csv',

    exportOptions: {},

    fieldSeparator: ',',

    fieldBoundary: '"',

    escapeChar: '"',

    charset: null,

    header: true,

    footer: false
  };

//
// Excel (xlsx) export
//
  DataTable.ext.buttons.excelHtml5 = {
    className: 'buttons-excel buttons-html5',

    available: function () {
      return window.FileReader !== undefined && _jsZip() !== undefined && !_isDuffSafari() && _serialiser;
    },

    text: function (dt) {
      return dt.i18n('buttons.excel', 'Excel');
    },

    action: function (e, dt, button, config) {
      this.processing(true);

      var that = this;
      var rowPos = 0;
      var dataStartRow, dataEndRow;
      var getXml = function (type) {
        var str = excelStrings[type];

        //str = str.replace( /xmlns:/g, 'xmlns_' ).replace( /mc:/g, 'mc_' );

        return $.parseXML(str);
      };
      var rels = getXml('xl/worksheets/sheet1.xml');
      var relsGet = rels.getElementsByTagName("sheetData")[0];

      var xlsx = {
        _rels: {
          ".rels": getXml('_rels/.rels')
        },
        xl: {
          _rels: {
            "workbook.xml.rels": getXml('xl/_rels/workbook.xml.rels')
          },
          "workbook.xml": getXml('xl/workbook.xml'),
          "styles.xml": getXml('xl/styles.xml'),
          "worksheets": {
            "sheet1.xml": rels
          }

        },
        "[Content_Types].xml": getXml('[Content_Types].xml')
      };

      var data = dt.buttons.exportData(config.exportOptions);
      var currentRow, rowNode;
      var addRow = function (row) {
        currentRow = rowPos + 1;
        rowNode = _createNode(rels, "row", {attr: {r: currentRow}});

        for (var i = 0, ien = row.length; i < ien; i++) {
          // Concat both the Cell Columns as a letter and the Row of the cell.
          var cellId = createCellPos(i) + '' + currentRow;
          var cell = null;

          // For null, undefined of blank cell, continue so it doesn't create the _createNode
          if (row[i] === null || row[i] === undefined || row[i] === '') {
            if (config.createEmptyCells === true) {
              row[i] = '';
            } else {
              continue;
            }
          }

          var originalContent = row[i];
          row[i] = typeof row[i].trim === 'function'
            ? row[i].trim()
            : row[i];

          // Special number formatting options
          for (var j = 0, jen = _excelSpecials.length; j < jen; j++) {
            var special = _excelSpecials[j];

            // TODO Need to provide the ability for the specials to say
            // if they are returning a string, since at the moment it is
            // assumed to be a number
            if (row[i].match && !row[i].match(/^0\d+/) && row[i].match(special.match)) {
              var val = row[i].replace(/[^\d\.\-]/g, '');

              if (special.fmt) {
                val = special.fmt(val);
              }

              cell = _createNode(rels, 'c', {
                attr: {
                  r: cellId,
                  s: special.style
                },
                children: [
                  _createNode(rels, 'v', {text: val})
                ]
              });

              break;
            }
          }

          if (!cell) {
            if (typeof row[i] === 'number' || (
              row[i].match &&
              row[i].match(/^-?\d+(\.\d+)?$/) &&
              !row[i].match(/^0\d+/))
            ) {
              // Detect numbers - don't match numbers with leading zeros
              // or a negative anywhere but the start
              cell = _createNode(rels, 'c', {
                attr: {
                  t: 'n',
                  r: cellId
                },
                children: [
                  _createNode(rels, 'v', {text: row[i]})
                ]
              });
            } else {
              // String output - replace non standard characters for text output
              var text = !originalContent.replace ?
                originalContent :
                originalContent.replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');

              cell = _createNode(rels, 'c', {
                attr: {
                  t: 'inlineStr',
                  r: cellId
                },
                children: {
                  row: _createNode(rels, 'is', {
                    children: {
                      row: _createNode(rels, 't', {
                        text: text,
                        attr: {
                          'xml:space': 'preserve'
                        }
                      })
                    }
                  })
                }
              });
            }
          }

          rowNode.appendChild(cell);
        }

        relsGet.appendChild(rowNode);
        rowPos++;
      };

      if (config.customizeData) {
        config.customizeData(data);
      }

      var mergeCells = function (row, colspan) {
        var mergeCells = $('mergeCells', rels);

        mergeCells[0].appendChild(_createNode(rels, 'mergeCell', {
          attr: {
            ref: 'A' + row + ':' + createCellPos(colspan) + row
          }
        }));
        mergeCells.attr('count', parseFloat(mergeCells.attr('count')) + 1);
        $('row:eq(' + (row - 1) + ') c', rels).attr('s', '51'); // centre
      };

      // Title and top messages
      var exportInfo = dt.buttons.exportInfo(config);
      if (exportInfo.title) {
        addRow([exportInfo.title], rowPos);
        mergeCells(rowPos, data.header.length - 1);
      }

      if (exportInfo.messageTop) {
        addRow([exportInfo.messageTop], rowPos);
        mergeCells(rowPos, data.header.length - 1);
      }


      // Table itself
      if (config.header) {
        addRow(data.header, rowPos);
        $('row:last c', rels).attr('s', '2'); // bold
      }

      dataStartRow = rowPos;

      for (var n = 0, ie = data.body.length; n < ie; n++) {
        addRow(data.body[n], rowPos);
      }

      dataEndRow = rowPos;

      if (config.footer && data.footer) {
        addRow(data.footer, rowPos);
        $('row:last c', rels).attr('s', '2'); // bold
      }

      // Below the table
      if (exportInfo.messageBottom) {
        addRow([exportInfo.messageBottom], rowPos);
        mergeCells(rowPos, data.header.length - 1);
      }

      // Set column widths
      var cols = _createNode(rels, 'cols');
      $('worksheet', rels).prepend(cols);

      for (var i = 0, ien = data.header.length; i < ien; i++) {
        cols.appendChild(_createNode(rels, 'col', {
          attr: {
            min: i + 1,
            max: i + 1,
            width: _excelColWidth(data, i),
            customWidth: 1
          }
        }));
      }

      // Workbook modifications
      var workbook = xlsx.xl['workbook.xml'];

      $('sheets sheet', workbook).attr('name', _sheetname(config));

      // Auto filter for columns
      if (config.autoFilter) {
        $('mergeCells', rels).before(_createNode(rels, 'autoFilter', {
          attr: {
            ref: 'A' + dataStartRow + ':' + createCellPos(data.header.length - 1) + dataEndRow
          }
        }));

        $('definedNames', workbook).append(_createNode(workbook, 'definedName', {
          attr: {
            name: '_xlnm._FilterDatabase',
            localSheetId: '0',
            hidden: 1
          },
          text: _sheetname(config) + '!$A$' + dataStartRow + ':' + createCellPos(data.header.length - 1) + dataEndRow
        }));
      }

      // Let the developer customise the document if they want to
      if (config.customize) {
        config.customize(xlsx, config, dt);
      }

      // Excel doesn't like an empty mergeCells tag
      if ($('mergeCells', rels).children().length === 0) {
        $('mergeCells', rels).remove();
      }

      var jszip = _jsZip();
      var zip = new jszip();
      var zipConfig = {
        type: 'blob',
        mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
      };

      _addToZip(zip, xlsx);

      if (zip.generateAsync) {
        // JSZip 3+
        zip
          .generateAsync(zipConfig)
          .then(function (blob) {
            _saveAs(blob, exportInfo.filename);
            that.processing(false);
          });
      } else {
        // JSZip 2.5
        _saveAs(
          zip.generate(zipConfig),
          exportInfo.filename
        );
        this.processing(false);
      }
    },

    filename: '*',

    extension: '.xlsx',

    exportOptions: {},

    header: true,

    footer: false,

    title: '*',

    messageTop: '*',

    messageBottom: '*',

    createEmptyCells: false,

    autoFilter: false,

    sheetName: ''
  };

//
// PDF export - using pdfMake - http://pdfmake.org
//
  DataTable.ext.buttons.pdfHtml5 = {
    className: 'buttons-pdf buttons-html5',

    available: function () {
      return window.FileReader !== undefined && _pdfMake();
    },

    text: function (dt) {
      return dt.i18n('buttons.pdf', 'PDF');
    },

    action: function (e, dt, button, config) {
      this.processing(true);

      var that = this;
      var data = dt.buttons.exportData(config.exportOptions);
      var info = dt.buttons.exportInfo(config);
      var rows = [];

      if (config.header) {
        rows.push($.map(data.header, function (d) {
          return {
            text: typeof d === 'string' ? d : d + '',
            style: 'tableHeader'
          };
        }));
      }

      for (var i = 0, ien = data.body.length; i < ien; i++) {
        rows.push($.map(data.body[i], function (d) {
          if (d === null || d === undefined) {
            d = '';
          }
          return {
            text: typeof d === 'string' ? d : d + '',
            style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
          };
        }));
      }

      if (config.footer && data.footer) {
        rows.push($.map(data.footer, function (d) {
          return {
            text: typeof d === 'string' ? d : d + '',
            style: 'tableFooter'
          };
        }));
      }

      var doc = {
        pageSize: config.pageSize,
        pageOrientation: config.orientation,
        content: [
          {
            table: {
              headerRows: 1,
              body: rows
            },
            layout: 'noBorders'
          }
        ],
        styles: {
          tableHeader: {
            bold: true,
            fontSize: 11,
            color: 'white',
            fillColor: '#2d4154',
            alignment: 'center'
          },
          tableBodyEven: {},
          tableBodyOdd: {
            fillColor: '#f3f3f3'
          },
          tableFooter: {
            bold: true,
            fontSize: 11,
            color: 'white',
            fillColor: '#2d4154'
          },
          title: {
            alignment: 'center',
            fontSize: 15
          },
          message: {}
        },
        defaultStyle: {
          fontSize: 10
        }
      };

      if (info.messageTop) {
        doc.content.unshift({
          text: info.messageTop,
          style: 'message',
          margin: [0, 0, 0, 12]
        });
      }

      if (info.messageBottom) {
        doc.content.push({
          text: info.messageBottom,
          style: 'message',
          margin: [0, 0, 0, 12]
        });
      }

      if (info.title) {
        doc.content.unshift({
          text: info.title,
          style: 'title',
          margin: [0, 0, 0, 12]
        });
      }

      if (config.customize) {
        config.customize(doc, config, dt);
      }

      var pdf = _pdfMake().createPdf(doc);

      if (config.download === 'open' && !_isDuffSafari()) {
        pdf.open();
      } else {
        pdf.download(info.filename);
      }

      this.processing(false);
    },

    title: '*',

    filename: '*',

    extension: '.pdf',

    exportOptions: {},

    orientation: 'portrait',

    pageSize: 'A4',

    header: true,

    footer: false,

    messageTop: '*',

    messageBottom: '*',

    customize: null,

    download: 'download'
  };


  return DataTable.Buttons;
}));
